package com.lixueju.security.box.web.server;

import com.lixueju.security.box.core.properties.OAuth2ClientProperties;
import com.lixueju.security.box.core.properties.SecurityBoxConstants;
import com.lixueju.security.box.core.properties.SecurityBoxProperties;
import com.lixueju.security.box.web.authentication.SecurityBoxTokenServices;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;

import javax.annotation.Resource;
import java.security.KeyPair;
import java.util.Arrays;

/**
 * 认证服务器配置
 *
 * @author lixueju
 * @since 2019/11/6 13:10
 **/
@Configuration
@EnableAuthorizationServer
public class SecurityBoxAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    //jwt令牌转换器
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    private AuthorizationServerTokenServices tokenServices;

    //@Autowired
    //TokenStore tokenStore;

    @Autowired
    private SecurityBoxProperties securityBoxProperties;

    //读取密钥的配置
    @Bean("keyProp")
    public KeyProperties keyProperties() {
        return new KeyProperties();
    }

    @Resource(name = "keyProp")
    private KeyProperties keyProperties;

    //token的存储方法
    @Bean
    //@Autowired
    public JwtTokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        if (jwtAccessTokenConverter != null) {
            return jwtAccessTokenConverter;
        }
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        if (keyProperties.getKeyStore().getLocation() == null) {
            KeyProperties.KeyStore keyStore = new KeyProperties.KeyStore();
            keyStore.setLocation(new ClassPathResource(SecurityBoxConstants.DEFAULT_KEY_STORE_LOCATION));
            keyStore.setAlias(SecurityBoxConstants.DEFAULT_KEY_STORE_ALIAS);
            keyStore.setPassword(SecurityBoxConstants.DEFAULT_KEY_STORE_PASSWORD);
            keyStore.setSecret(SecurityBoxConstants.DEFAULT_KEY_STORE_SECRET);
            keyProperties.setKeyStore(keyStore);
        }
        KeyPair keyPair = new KeyStoreKeyFactory
                (keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getSecret().toCharArray())
                .getKeyPair(keyProperties.getKeyStore().getAlias(), keyProperties.getKeyStore().getPassword().toCharArray());
        converter.setKeyPair(keyPair);
        this.jwtAccessTokenConverter = converter;
        return this.jwtAccessTokenConverter;
    }

    /**
     * 认证及token配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenServices(tokenServices())// 自定义的tokenServices
                .accessTokenConverter(jwtAccessTokenConverter())
                .authenticationManager(authenticationManager)//认证管理器
                .tokenStore(tokenStore())//令牌存储
                .userDetailsService(userDetailsService);//用户信息service

    }

    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        if (tokenServices != null) {
            return tokenServices;
        }
        SecurityBoxTokenServices tokenServices = new SecurityBoxTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);//支持刷新token
        tokenServices.setReuseRefreshToken(true);
        tokenServices.setClientDetailsService(clientDetailsService);
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        tokenServices.setAuthenticationManager(this.authenticationManager);
        addUserDetailsService(tokenServices, this.userDetailsService);
        this.tokenServices = tokenServices;
        return tokenServices;
    }

    private void addUserDetailsService(SecurityBoxTokenServices tokenServices, UserDetailsService userDetailsService) {
        if (userDetailsService != null) {
            PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
            provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(
                    userDetailsService));
            tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider)));
        }
    }

    /**
     * tokenKey的访问权限表达式配置
     */
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 客户端配置
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if (ArrayUtils.isNotEmpty(securityBoxProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties client : securityBoxProperties.getOauth2().getClients()) {
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .accessTokenValiditySeconds(client.getTokenValiditySeconds())
                        .refreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds())
                        .scopes(client.getScopes());
            }
        }
        OAuth2ClientProperties client = securityBoxProperties.getOauth2().getClient();
        builder.withClient(client.getClientId())
                .secret(client.getClientSecret())
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                .accessTokenValiditySeconds(client.getTokenValiditySeconds())
                .refreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds())
                .scopes(client.getScopes());
    }

    //    @Bean
//    @Primary
//    public DefaultTokenServices tokenServices() {
//        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//        defaultTokenServices.setTokenStore(tokenStore());
//        defaultTokenServices.setSupportRefreshToken(true);
//        return defaultTokenServices;
//    }
}
